package com.eenie.common.utils

import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.OnLifecycleEvent
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import java.util.concurrent.TimeUnit

open class TimeCounter(var time: Long, var unit: TimeUnit, var isCountDown: Boolean = false) : LifecycleObserver {
    private val TIME_STATE_STOP = 0
    private val TIME_STATE_RUNNING = 1
    private val TIME_STATE_PAUSED = 2

    private var mTolTime: Long = 0
    private var mCurTime: Long = 0
    private var mStepTime: Long = 1000

    private var mTimerState = 0

    private var mAutoStartResume = false

    private var mCounter: Observable<Long>? = null
    private var mDisposable: Disposable? = null


    private var mTimerCounterListener: TimerCounterListener? = null

    init {
        mTolTime = unit.toMillis(time)
        mCounter = createTimer(mTolTime)
        this.isCountDown = isCountDown
    }

    fun setStepTime(time: Long, unit: TimeUnit) {
        mStepTime = unit.toMillis(time)
    }

    private fun createTimer(timeMilliSeconds: Long): Observable<Long> {
        return Observable.just(timeMilliSeconds)
                .delay(mStepTime, TimeUnit.MILLISECONDS)
                .subscribeOn(Schedulers.io())
                .repeatUntil {
                    if (mTimerState == TIME_STATE_RUNNING) {
                        mCurTime = mCurTime + mStepTime
                    }
                    mCurTime >= mTolTime + mStepTime || mTimerState == TIME_STATE_STOP
                }
                .map {
                    if (isCountDown) mTolTime - mCurTime else mCurTime
                }
                .doOnSubscribe {
                    if (mTimerCounterListener != null) {
                        mTimerCounterListener!!.onTick(if (isCountDown) mTolTime else 0, mTolTime)
                        mCurTime += mStepTime
                    }
                }
                .observeOn(AndroidSchedulers.mainThread())
    }


    fun startCount() {
        if (mTimerState == TIME_STATE_PAUSED) {
            resumeCount()
        }else if (mTimerState == TIME_STATE_RUNNING) {
            //do nothing
        } else {
            mTimerState = TIME_STATE_RUNNING
            mDisposable = mCounter!!.subscribe { time ->
                if (mTimerCounterListener != null) {
                    if (isCountDown) {
                        if (time <= 0) {
                            resetCount()
                            mTimerCounterListener!!.onComplete()
                        } else {
                            if (mTimerState == TIME_STATE_RUNNING) {
                                mTimerCounterListener!!.onTick(time, mTolTime)
                            }
                        }
                    } else {
                        if (time >= mTolTime) {
                            resetCount()
                            mTimerCounterListener!!.onComplete()
                        } else {
                            if (mTimerState == TIME_STATE_RUNNING) {
                                mTimerCounterListener!!.onTick(time, mTolTime)
                            }
                        }
                    }
                }
            }
        }
    }

    fun pauseCount() {
        mTimerState = TIME_STATE_PAUSED
    }


    fun resumeCount() {
        if (mDisposable != null && !mDisposable!!.isDisposed && mTimerState == TIME_STATE_PAUSED) {
            mTimerState = TIME_STATE_RUNNING
        }
    }

    fun resetCount() {
        resetCount(mTolTime, TimeUnit.MILLISECONDS)
    }

    fun isRunning(): Boolean {
        return mDisposable != null && !mDisposable!!.isDisposed && mTimerState == TIME_STATE_RUNNING
    }

    fun resetCount(time: Long, unit: TimeUnit) {
        if (mDisposable != null && !mDisposable!!.isDisposed) {
            mDisposable!!.dispose()
        }
        mCurTime = 0
        mDisposable = null
        mTolTime = unit.toMillis(time)
        mTimerState = TIME_STATE_STOP
        mCounter = createTimer(mTolTime)
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    fun release() {
        if (mDisposable != null && !mDisposable!!.isDisposed) {
            mDisposable!!.dispose()
            mDisposable = null
        }
        mTimerState = TIME_STATE_STOP
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    private fun autoResume() {
        if (mAutoStartResume) {
            resumeCount()
        }
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    private fun autoStop() {
        if (mAutoStartResume) {
            pauseCount()
        }
    }


    interface TimerCounterListener {
        fun onTick(curTime: Long, tolTime: Long)
        fun onComplete()
    }

    fun setTimerCounterListener(listener: TimerCounterListener?) {
        mTimerCounterListener = listener
    }


    fun bindLifecycle(lifecycleOwner: LifecycleOwner) {
        lifecycleOwner.lifecycle.addObserver(this)
    }


    fun getCurTime(): Long {
        return mCurTime
    }


    fun setAutoStartResume(auto: Boolean) {
        mAutoStartResume = auto
    }
}